home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / Auth / HTTP.php < prev    next >
Encoding:
PHP Script  |  2005-07-07  |  23.9 KB  |  796 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Martin Jansen <mj@php.net>                                  |
  17. // |          Rui Hirokawa <hirokawa@php.net>                             |
  18. // |          David Costa  <gurugeek@php.net>                             |
  19. // +----------------------------------------------------------------------+
  20. //
  21. //  $Id: Auth_HTTP.php,v 1.27 2005/04/04 12:48:33 hirokawa Exp $ 
  22. //
  23.  
  24. require_once "Auth/Auth.php";
  25.  
  26. define('AUTH_HTTP_NONCE_TIME_LEN', 16);
  27. define('AUTH_HTTP_NONCE_HASH_LEN', 32);
  28.  
  29. // {{{ class Auth_HTTP
  30.  
  31. /**
  32.  * PEAR::Auth_HTTP
  33.  *
  34.  * The PEAR::Auth_HTTP class provides methods for creating an
  35.  * HTTP authentication system based on RFC-2617 using PHP.
  36.  *
  37.  * Instead of generating an HTML driven form like PEAR::Auth
  38.  * does, this class sends header commands to the clients which
  39.  * cause them to present a login box like they are e.g. used
  40.  * in Apache's .htaccess mechanism.
  41.  *
  42.  * This class requires the PEAR::Auth package.
  43.  *
  44.  * @notes The HTTP Digest Authentication part is based on
  45.  *  authentication class written by Tom Pike <tom.pike@xiven.com>
  46.  *
  47.  * @author  Martin Jansen <mj@php.net>
  48.  * @author  Rui Hirokawa <hirokawa@php.net>
  49.  * @author  David Costa <gurugeek@php.net>
  50.  * @package Auth_HTTP
  51.  * @extends Auth
  52.  * @version $Revision: 1.27 $
  53.  */
  54. class Auth_HTTP extends Auth
  55. {
  56.    
  57.     // {{{ properties
  58.  
  59.     /**
  60.      * Authorization method: 'basic' or 'digest'
  61.      *
  62.      * @access public
  63.      * @var    string
  64.      */
  65.     var $authType = 'basic';
  66.  
  67.     /**
  68.      * Name of the realm for Basic Authentication
  69.      *
  70.      * @access public
  71.      * @var    string
  72.      * @see    drawLogin()
  73.      */
  74.     var $realm = "protected area";
  75.  
  76.     /**
  77.      * Text to send if user hits cancel button
  78.      *
  79.      * @access public
  80.      * @var    string
  81.      * @see    drawLogin()
  82.      */
  83.     var $CancelText = "Error 401 - Access denied";
  84.  
  85.     /**
  86.      * option array
  87.      *
  88.      * @access public
  89.      * @var    array
  90.      */
  91.     var $options = array();
  92.  
  93.     /**
  94.      * flag to indicate the nonce was stale. 
  95.      *
  96.      * @access public
  97.      * @var    bool
  98.      */
  99.     var $stale = false;
  100.  
  101.     /**
  102.      * opaque string for digest authentication
  103.      *
  104.      * @access public
  105.      * @var    string
  106.      */
  107.     var $opaque = 'dummy';
  108.  
  109.     /**
  110.      * digest URI
  111.      *
  112.      * @access public
  113.      * @var    string
  114.      */
  115.     var $uri = '';
  116.  
  117.     /**
  118.      * authorization info returned by the client
  119.      *
  120.      * @access public
  121.      * @var    array
  122.      */
  123.     var $auth = array();
  124.  
  125.     /**
  126.      * next nonce value
  127.      *
  128.      * @access public
  129.      * @var    string
  130.      */
  131.     var $nextNonce = '';
  132.  
  133.     /**
  134.      * nonce value
  135.      *
  136.      * @access public
  137.      * @var    string
  138.      */
  139.     var $nonce = '';
  140.  
  141.     /**
  142.      * Holds a reference to the global server variable
  143.      * @var array
  144.      */
  145.     var $server;
  146.  
  147.     /**
  148.      * Holds a reference to the global post variable
  149.      * @var array
  150.      */
  151.     var $post;
  152.  
  153.     /**
  154.      * Holds a reference to the global cookie variable
  155.      * @var array
  156.      */
  157.     var $cookie;
  158.  
  159.  
  160.     // }}}
  161.     // {{{ Constructor
  162.  
  163.     /**
  164.      * Constructor
  165.      *
  166.      * @param string    Type of the storage driver
  167.      * @param mixed     Additional options for the storage driver
  168.      *                  (example: if you are using DB as the storage
  169.      *                   driver, you have to pass the dsn string here)
  170.      *
  171.      * @return void
  172.      */
  173.     function Auth_HTTP($storageDriver, $options = '') 
  174.     {
  175.         /* set default values for options */
  176.         $this->options = array('cryptType' => 'md5',
  177.                                'algorithm' => 'MD5',
  178.                                'qop' => 'auth-int,auth',
  179.                                'opaquekey' => 'moo',
  180.                                'noncekey' => 'moo',
  181.                                'digestRealm' => 'protected area',
  182.                                'forceDigestOnly' => false,
  183.                                'nonceLife' => 300,
  184.                                'sessionSharing' => true,
  185.                                );
  186.         
  187.         if (!empty($options['authType'])) {
  188.             $this->authType = strtolower($options['authType']);
  189.         }
  190.         
  191.         if (is_array($options)) {
  192.             foreach($options as $key => $value) {
  193.                 if (array_key_exists( $key, $this->options)) {
  194.                     $this->options[$key] = $value;
  195.                 }
  196.             }
  197.         
  198.             if (!empty($this->options['opaquekey'])) {
  199.                 $this->opaque = md5($this->options['opaquekey']);
  200.             }
  201.         }
  202.         
  203.         $this->Auth($storageDriver, $options);
  204.     }
  205.     
  206.     // }}}
  207.     // {{{ assignData()
  208.  
  209.     /**
  210.      * Assign values from $PHP_AUTH_USER and $PHP_AUTH_PW or 'Authorization' header
  211.      * to internal variables and sets the session id based
  212.      * on them
  213.      *
  214.      * @access public
  215.      * @return void
  216.      */
  217.     function assignData()
  218.     {
  219.         if (method_exists($this, '_importGlobalVariable')) {
  220.             $this->server = &$this->_importGlobalVariable('server');
  221.         }
  222.         
  223.         
  224.         if ($this->authType == 'basic') {
  225.             if (!empty($this->server['PHP_AUTH_USER'])) {
  226.                 $this->username = $this->server['PHP_AUTH_USER'];
  227.             }
  228.             
  229.             if (!empty($this->server['PHP_AUTH_PW'])) {
  230.                 $this->password = $this->server['PHP_AUTH_PW'];
  231.             }
  232.             
  233.             /**
  234.              * Try to get authentication information from IIS
  235.              */
  236.             if  (empty($this->username) && empty($this->password)) {
  237.                 if (!empty($this->server['HTTP_AUTHORIZATION'])) {
  238.                     list($this->username, $this->password) = 
  239.                         explode(':', base64_decode(substr($this->server['HTTP_AUTHORIZATION'], 6)));
  240.                 }
  241.             }
  242.         } elseif ($this->authType == 'digest') {
  243.             $this->username = '';
  244.             $this->password = '';
  245.  
  246.             $this->digest_header = null;
  247.             if (!empty($this->server['PHP_AUTH_DIGEST'])) {
  248.                 $this->digest_header = substr($this->server['PHP_AUTH_DIGEST'],
  249.                                               strpos($this->server['PHP_AUTH_DIGEST'],' ')+1);
  250.             } else {
  251.                 $headers = getallheaders();
  252.                 if(isset($headers['Authorization']) && !empty($headers['Authorization'])) {
  253.                     $this->digest_header = substr($headers['Authorization'],
  254.                                                   strpos($headers['Authorization'],' ')+1);
  255.                 }
  256.             }
  257.  
  258.             if($this->digest_header) {
  259.                 $authtemp = explode(',', $this->digest_header);
  260.                 $auth = array();
  261.                 foreach($authtemp as $key => $value) {
  262.                     $value = trim($value);
  263.                     if(strpos($value,'=') !== false) {
  264.                         $lhs = substr($value,0,strpos($value,'='));
  265.                         $rhs = substr($value,strpos($value,'=')+1);
  266.                         if(substr($rhs,0,1) == '"' && substr($rhs,-1,1) == '"') {
  267.                             $rhs = substr($rhs,1,-1);
  268.                         }
  269.                         $auth[$lhs] = $rhs;
  270.                     }
  271.                 }
  272.             }
  273.             if (!isset($auth['uri']) || !isset($auth['realm'])) {
  274.                 return;
  275.             }
  276.             
  277.             if ($this->selfURI() == $auth['uri']) {
  278.                 $this->uri = $auth['uri'];
  279.                 if (substr($headers['Authorization'],0,7) == 'Digest ') {
  280.                     
  281.                     $this->authType = 'digest';
  282.  
  283.                     if (!isset($auth['nonce']) || !isset($auth['username']) || 
  284.                   !isset($auth['response']) || !isset($auth['qop']) || 
  285.                   !isset($auth['nc']) || !isset($auth['cnonce'])){
  286.                         return;
  287.                     }
  288.  
  289.                if ($auth['qop'] != 'auth' && $auth['qop'] != 'auth-int') {
  290.                         return;
  291.                }
  292.                     
  293.                     $this->stale = $this->_judgeStale($auth['nonce']);
  294.  
  295.                if ($this->nextNonce == false) {
  296.                   return;
  297.                }
  298.  
  299.                     $this->username = $auth['username'];
  300.                     $this->password = $auth['response'];
  301.                     $this->auth['nonce'] = $auth['nonce'];
  302.                     
  303.                $this->auth['qop'] = $auth['qop'];
  304.                $this->auth['nc'] = $auth['nc'];
  305.                $this->auth['cnonce'] = $auth['cnonce'];
  306.  
  307.                     if (isset($auth['opaque'])) {
  308.                         $this->auth['opaque'] = $auth['opaque'];
  309.                     }
  310.                     
  311.                 } elseif (substr($headers['Authorization'],0,6) == 'Basic ') {
  312.                     if ($this->options['forceDigestOnly']) {
  313.                         return; // Basic authentication is not allowed.
  314.                     }
  315.                     
  316.                     $this->authType = 'basic';
  317.                     list($username, $password) = 
  318.                         explode(':',base64_decode(substr($headers['Authorization'],6)));
  319.                     $this->username = $username;
  320.                     $this->password = $password;
  321.                 }
  322.             }
  323.         } else {
  324.             return PEAR::raiseError('authType is invalid.');
  325.         }
  326.  
  327.         if ($this->options['sessionSharing'] && 
  328.             isset($this->username) && isset($this->password)) {
  329.             session_id(md5('Auth_HTTP' . $this->username . $this->password));
  330.         }
  331.         
  332.         /**
  333.          * set sessionName for AUTH, so that the sessionName is different 
  334.          * for distinct realms 
  335.          */
  336.          $this->_sessionName = "_authhttp".md5($this->realm);
  337.     }
  338.  
  339.     // }}}
  340.     // {{{ login()
  341.  
  342.     /**
  343.      * Login function
  344.      *
  345.      * @access private
  346.      * @return void
  347.      */
  348.     function login() 
  349.     {
  350.         $login_ok = false;
  351.         if (method_exists($this, '_loadStorage')) {
  352.             $this->_loadStorage();
  353.         }
  354.         $this->storage->_auth_obj->_sessionName =& $this->_sessionName;
  355.  
  356.         /**
  357.          * When the user has already entered a username,
  358.          * we have to validate it.
  359.          */
  360.         if (!empty($this->username) && !empty($this->password)) {
  361.             if ($this->authType == 'basic' && !$this->options['forceDigestOnly']) {
  362.                 if (true === $this->storage->fetchData($this->username, $this->password)) {
  363.                     $login_ok = true;
  364.                 }
  365.             } else { /* digest authentication */
  366.  
  367.                 if (!$this->getAuth() || $this->getAuthData('a1') == null) {
  368.                     /* 
  369.                      * note:
  370.                      *  - only PEAR::DB is supported as container.
  371.                      *  - password should be stored in container as plain-text 
  372.                      *    (if $options['cryptType'] == 'none') or 
  373.                      *     A1 hashed form (md5('username:realm:password')) 
  374.                      *    (if $options['cryptType'] == 'md5')
  375.                      */
  376.                     $dbs = $this->storage;
  377.                     if (!DB::isConnection($dbs->db)) {
  378.                         $dbs->_connect($dbs->options['dsn']);
  379.                     }
  380.                     
  381.                     $query = 'SELECT '.$dbs->options['passwordcol']." FROM ".$dbs->options['table'].
  382.                         ' WHERE '.$dbs->options['usernamecol']." = '".
  383.                         $dbs->db->quoteString($this->username)."' ";
  384.                     
  385.                     $pwd = $dbs->db->getOne($query); // password stored in container.
  386.                     
  387.                     if (DB::isError($pwd)) {
  388.                         return PEAR::raiseError($pwd->getMessage(), $pwd->getCode());
  389.                     }
  390.                     
  391.                     if ($this->options['cryptType'] == 'none') {
  392.                         $a1 = md5($this->username.':'.$this->options['digestRealm'].':'.$pwd);
  393.                     } else {
  394.                         $a1 = $pwd;
  395.                     }
  396.                     
  397.                     $this->setAuthData('a1', $a1, true);
  398.                 } else {
  399.                     $a1 = $this->getAuthData('a1');
  400.                 }
  401.                 
  402.                 $login_ok = $this->validateDigest($this->password, $a1);
  403.                 if ($this->nextNonce == false) {
  404.                     $login_ok = false;
  405.                 }
  406.             }
  407.             
  408.             if (!$login_ok && is_callable($this->loginFailedCallback)) {
  409.                 call_user_func($this->loginFailedCallback,$this->username, $this);
  410.             }
  411.         }
  412.         
  413.         if (!empty($this->username) && $login_ok) {
  414.             $this->setAuth($this->username);
  415.             if (is_callable($this->loginCallback)) {
  416.                 call_user_func($this->loginCallback,$this->username, $this);
  417.             }
  418.         }
  419.         
  420.         /**
  421.          * If the login failed or the user entered no username,
  422.          * output the login screen again.
  423.          */
  424.         if (!empty($this->username) && !$login_ok) {
  425.             $this->status = AUTH_WRONG_LOGIN;
  426.         }
  427.         
  428.         if ((empty($this->username) || !$login_ok) && $this->showLogin) {
  429.             $this->drawLogin($this->storage->activeUser);
  430.             return;
  431.         }
  432.  
  433.       if (!empty($this->username) && $login_ok && $this->authType == 'digest'
  434.          && $this->auth['qop'] == 'auth') { 
  435.          $this->authenticationInfo();
  436.       }
  437.     }
  438.     
  439.     // }}}
  440.     // {{{ drawLogin()
  441.  
  442.     /**
  443.      * Launch the login box
  444.      *
  445.      * @param  string $username  Username
  446.      * @return void
  447.      * @access private
  448.      */
  449.     function drawLogin($username = "")
  450.     {
  451.         /**
  452.          * Send the header commands
  453.          */
  454.         if ($this->authType == 'basic') {
  455.             header("WWW-Authenticate: Basic realm=\"".$this->realm."\"");
  456.             header('HTTP/1.0 401 Unauthorized');            
  457.         } else if ($this->authType == 'digest') {
  458.             $this->nonce = $this->_getNonce();
  459.  
  460.             $wwwauth = 'WWW-Authenticate: Digest ';
  461.             $wwwauth .= 'qop="'.$this->options['qop'].'", ';
  462.             $wwwauth .= 'algorithm='.$this->options['algorithm'].', ';
  463.             $wwwauth .= 'realm="'.$this->options['digestRealm'].'", ';
  464.             $wwwauth .= 'nonce="'.$this->nonce.'", ';
  465.             if ($this->stale) {
  466.                 $wwwauth .= 'stale=true, ';
  467.             }
  468.             if (!empty($this->opaque)) {
  469.                 $wwwauth .= 'opaque="'.$this->opaque.'"' ;
  470.             }
  471.             $wwwauth .= "\r\n";
  472.             if (!$this->options['forceDigestOnly']) {
  473.                 $wwwauth .= 'WWW-Authenticate: Basic realm="'.$this->realm.'"';
  474.             }
  475.             header($wwwauth);
  476.             header('HTTP/1.0 401 Unauthorized');            
  477.         }
  478.  
  479.         /**
  480.          * This code is only executed if the user hits the cancel
  481.          * button or if he enters wrong data 3 times.
  482.          */
  483.         if ($this->stale) {
  484.             echo 'Stale nonce value, please re-authenticate.';
  485.         } else {
  486.             echo $this->CancelText;
  487.         }
  488.         exit;
  489.     }
  490.  
  491.     // }}}
  492.     // {{{ setRealm()
  493.  
  494.     /**
  495.      * Set name of the current realm
  496.      *
  497.      * @access public
  498.      * @param  string $realm  Name of the realm
  499.      * @param  string $digestRealm  Name of the realm for digest authentication
  500.      * @return void
  501.      */
  502.     function setRealm($realm, $digestRealm = '')
  503.     {
  504.         $this->realm = $realm;
  505.         if (!empty($digestRealm)) {
  506.             $this->options['digestRealm'] = $digestRealm;
  507.         }
  508.     }
  509.  
  510.     // }}}
  511.     // {{{ setCancelText()
  512.  
  513.     /**
  514.      * Set the text to send if user hits the cancel button
  515.      *
  516.      * @access public
  517.      * @param  string $text  Text to send
  518.      * @return void
  519.      */
  520.     function setCancelText($text)
  521.     {
  522.         $this->CancelText = $text;
  523.     }
  524.  
  525.     // }}}
  526.     // {{{ validateDigest()
  527.     
  528.     /**
  529.      * judge if the client response is valid.
  530.      *
  531.      * @access private
  532.      * @param  string $response  client response
  533.      * @param  string $a1 password or hashed password stored in container
  534.      * @return bool true if success, false otherwise
  535.      */
  536.     function validateDigest($response, $a1)    
  537.     {
  538.         if (method_exists($this, '_importGlobalVariable')) {
  539.             $this->server = &$this->_importGlobalVariable('server');
  540.         }
  541.  
  542.         $a2unhashed = $this->server['REQUEST_METHOD'].":".$this->selfURI();
  543.         if($this->auth['qop'] == 'auth-int') {
  544.             if(isset($GLOBALS["HTTP_RAW_POST_DATA"])) {
  545.                 // In PHP < 4.3 get raw POST data from this variable
  546.                 $body = $GLOBALS["HTTP_RAW_POST_DATA"];
  547.             } else if($lines = @file('php://input')) {
  548.                 // In PHP >= 4.3 get raw POST data from this file
  549.                 $body = implode("\n", $lines);
  550.             } else {
  551.                 if (method_exists($this, '_importGlobalVariable')) {
  552.                     $this->post = &$this->_importGlobalVariable('post');
  553.                 }
  554.                 $body = '';
  555.                 foreach($this->post as $key => $value) {
  556.                     if($body != '') $body .= '&';
  557.                     $body .= rawurlencode($key) . '=' . rawurlencode($value);
  558.                 }
  559.             }
  560.  
  561.             $a2unhashed .= ':'.md5($body);
  562.         }
  563.         
  564.         $a2 = md5($a2unhashed);
  565.         $combined = $a1.':'.
  566.             $this->auth['nonce'].':'.
  567.             $this->auth['nc'].':'.
  568.             $this->auth['cnonce'].':'.
  569.             $this->auth['qop'].':'.
  570.             $a2;
  571.         $expectedResponse = md5($combined);
  572.         
  573.         if(!isset($this->auth['opaque']) || $this->auth['opaque'] == $this->opaque) {
  574.             if($response == $expectedResponse) { // password is valid
  575.                 if(!$this->stale) {
  576.                     return true;
  577.                 } else {
  578.                     $this->drawLogin();
  579.                 }
  580.             }
  581.         }
  582.         
  583.         return false;
  584.     }
  585.     
  586.     // }}}
  587.     // {{{ _judgeStale()
  588.     
  589.     /**
  590.      * judge if nonce from client is stale.
  591.      *
  592.      * @access private
  593.      * @param  string $nonce  nonce value from client
  594.      * @return bool stale
  595.      */
  596.     function _judgeStale($nonce) 
  597.     {
  598.         $stale = false;
  599.         
  600.         if(!$this->_decodeNonce($nonce, $time, $hash_cli)) {
  601.          $this->nextNonce = false;
  602.          $stale = true;
  603.             return $stale;
  604.         }
  605.  
  606.         if ($time < time() - $this->options['nonceLife']) {
  607.          $this->nextNonce = $this->_getNonce();
  608.             $stale = true;
  609.         } else {
  610.          $this->nextNonce = $nonce;
  611.       }
  612.  
  613.         return $stale;
  614.     }
  615.     
  616.     // }}}
  617.     // {{{ _nonceDecode()
  618.     
  619.     /**
  620.      * decode nonce string
  621.      *
  622.      * @access private
  623.      * @param  string $nonce nonce value from client
  624.      * @param  string $time decoded time
  625.      * @param  string $hash decoded hash
  626.      * @return bool false if nonce is invalid
  627.      */
  628.     function _decodeNonce($nonce, &$time, &$hash) 
  629.     {
  630.         if (method_exists($this, '_importGlobalVariable')) {
  631.             $this->server = &$this->_importGlobalVariable('server');
  632.         }
  633.  
  634.         if (strlen($nonce) != AUTH_HTTP_NONCE_TIME_LEN + AUTH_HTTP_NONCE_HASH_LEN) {
  635.             return false;
  636.         }
  637.  
  638.         $time =  base64_decode(substr($nonce, 0, AUTH_HTTP_NONCE_TIME_LEN));
  639.         $hash_cli = substr($nonce, AUTH_HTTP_NONCE_TIME_LEN, AUTH_HTTP_NONCE_HASH_LEN);
  640.  
  641.         $hash = md5($time . $this->server['HTTP_USER_AGENT'] . $this->options['noncekey']);
  642.  
  643.         if ($hash_cli != $hash) {
  644.             return false;
  645.         }
  646.         
  647.         return true;
  648.     }
  649.  
  650.     // }}}
  651.     // {{{ _getNonce()
  652.     
  653.     /**
  654.      * return nonce to detect timeout
  655.      *
  656.      * @access private
  657.      * @return string nonce value
  658.      */
  659.     function _getNonce() 
  660.     {
  661.         if (method_exists($this, '_importGlobalVariable')) {
  662.             $this->server = &$this->_importGlobalVariable('server');
  663.         }
  664.  
  665.         $time = time();
  666.         $hash = md5($time . $this->server['HTTP_USER_AGENT'] . $this->options['noncekey']);
  667.  
  668.         return base64_encode($time) . $hash;  
  669.     }
  670.  
  671.     // }}}
  672.     // {{{ authenticationInfo()
  673.     
  674.     /**
  675.      * output HTTP Authentication-Info header
  676.      *
  677.      * @notes md5 hash of contents is required if 'qop' is 'auth-int'
  678.      *
  679.      * @access private
  680.      * @param string MD5 hash of content
  681.      */
  682.     function authenticationInfo($contentMD5 = '') {
  683.         
  684.         if($this->getAuth() && ($this->getAuthData('a1') != null)) {
  685.             $a1 = $this->getAuthData('a1');
  686.  
  687.             // Work out authorisation response
  688.             $a2unhashed = ":".$this->selfURI();
  689.             if($this->auth['qop'] == 'auth-int') {
  690.                 $a2unhashed .= ':'.$contentMD5;
  691.             }
  692.             $a2 = md5($a2unhashed);
  693.             $combined = $a1.':'.
  694.                         $this->nonce.':'.
  695.                         $this->auth['nc'].':'.
  696.                         $this->auth['cnonce'].':'.
  697.                         $this->auth['qop'].':'.
  698.                         $a2;
  699.             
  700.             // Send authentication info
  701.             $wwwauth = 'Authentication-Info: ';
  702.             if($this->nonce != $this->nextNonce) {
  703.                 $wwwauth .= 'nextnonce="'.$this->nextNonce.'", ';
  704.             }
  705.             $wwwauth .= 'qop='.$this->auth['qop'].', ';
  706.             $wwwauth .= 'rspauth="'.md5($combined).'", ';
  707.             $wwwauth .= 'cnonce="'.$this->auth['cnonce'].'", ';
  708.             $wwwauth .= 'nc='.$this->auth['nc'].'';
  709.             header($wwwauth);
  710.         }
  711.     }
  712.     // }}}
  713.     // {{{ setOption()
  714.     /**
  715.      * set authentication option
  716.      *
  717.      * @access public
  718.      * @param mixed $name key of option
  719.      * @param mixed $value value of option
  720.      * @return void
  721.      */
  722.     function setOption($name, $value = null) 
  723.     {
  724.         if (is_array($name)) {
  725.             foreach($name as $key => $value) {
  726.                 if (array_key_exists( $key, $this->options)) {
  727.                     $this->options[$key] = $value;
  728.                 }
  729.             }
  730.         } else {
  731.             if (array_key_exists( $name, $this->options)) {
  732.                     $this->options[$name] = $value;
  733.             }
  734.         }
  735.     }
  736.  
  737.     // }}}
  738.     // {{{ getOption()
  739.     /**
  740.      * get authentication option
  741.      *
  742.      * @access public
  743.      * @param string $name key of option
  744.      * @return mixed option value
  745.      */
  746.     function getOption($name) 
  747.     {
  748.         if (array_key_exists( $name, $this->options)) {
  749.             return $this->options[$name];
  750.         }
  751.         if ($name == 'CancelText') {
  752.             return $this->CancelText;
  753.         }
  754.         if ($name == 'Realm') {
  755.             return $this->realm;
  756.         }
  757.         return false;
  758.     }
  759.  
  760.     // }}}
  761.     // {{{ selfURI()
  762.     /**
  763.      * get self URI
  764.      *
  765.      * @access public
  766.      * @return string self URI
  767.      */
  768.     function selfURI() 
  769.     {
  770.         if (method_exists($this, '_importGlobalVariable')) {
  771.             $this->server = &$this->_importGlobalVariable('server');
  772.         }
  773.  
  774.         if (preg_match("/MSIE/",$this->server['HTTP_USER_AGENT'])) {
  775.             // query string should be removed for MSIE
  776.             $uri = preg_replace("/^(.*)\?/","\\1",$this->server['REQUEST_URI']);
  777.         } else {
  778.             $uri = $this->server['REQUEST_URI'];
  779.         }
  780.         return $uri;
  781.     }
  782.  
  783.     // }}}
  784.  
  785. }
  786.  
  787. // }}}
  788.  
  789. /*
  790.  * Local variables:
  791.  * tab-width: 4
  792.  * c-basic-offset: 4
  793.  * End:
  794.  */
  795. ?>
  796.